/*
 * @(#)JPushUtil.java Created on 2015年7月1日上午2:05:32
 * 
 * 版权：版权所有 chinnsii 保留所有权力。
 */
package com.bsoft.gol.hbs.bbpmessagel.notification.event;

import com.bsoft.gol.hbs.bbpmessagel.notification.bean.Alert;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Options;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import cn.jpush.api.schedule.ScheduleResult;
import ctd.util.JSONUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author <a href="mailto:rishyonn@gmail.com">zhengshi</a>
 */
public class JPushInstance {

    private final Logger logger = LoggerFactory.getLogger(JPushInstance.class);

    private JPushClient jpushClient;
    private int retryCount = 5;
    private boolean apnsProduction;

    public JPushInstance(String appKey, String masterSecret, boolean apnsProduction) {
        jpushClient = new JPushClient(masterSecret, appKey);
        this.apnsProduction = apnsProduction;
    }

    /**
     * 向所有人发送消息
     *
     * @param alert
     * @param badge
     * @return 消息id
     */
    public long sendPushAll(Alert alert, Integer badge) {
        PushPayload payload = buildAllPayload(alert, badge);
        return send(payload);
    }

    /**
     * @param alert
     * @param badge
     * @param time
     * @return
     */
    public boolean schedulePushAll(Alert alert, Integer badge, Date time) {
        PushPayload payload = buildAllPayload(alert, badge);
        return schedule(time, payload);
    }

    /**
     * @param alert
     * @param badge
     * @param registrationIds
     * @return
     */
    public long sendPushAll(Alert alert, Integer badge, Set<String> registrationIds) {
        PushPayload payload = buildAllPayload(alert, badge, registrationIds);
        return send(payload);
    }

    /**
     * @param alert
     * @param badge
     * @param time
     * @param registrationIds
     * @return
     */
    public boolean schedulePushAll(Alert alert, Integer badge, Date time, Set<String> registrationIds) {
        PushPayload payload = buildAllPayload(alert, badge, registrationIds);
        return schedule(time, payload);
    }

    /**
     * @param alert
     * @param badge
     * @param registrationIds
     * @return
     */
    public long sendPushAll(Alert alert, Integer badge, String... registrationIds) {
        PushPayload payload = buildAllPayload(alert, badge, registrationIds);
        return send(payload);
    }

    /**
     * @param alert
     * @param badge
     * @param time
     * @param registrationIds
     * @return
     */
    public boolean schedulePushAll(Alert alert, Integer badge, Date time, String... registrationIds) {
        PushPayload payload = buildAllPayload(alert, badge, registrationIds);
        return schedule(time, payload);
    }

    /**
     * @param alert
     * @param registrationIds
     * @return
     */
    public long sendAndroidByAlias(Alert alert, Set<String> registrationIds) {
        PushPayload payload = buildAndroidPayload(alert, registrationIds);
        return send(payload);
    }

    /**
     * @param alert
     * @param time
     * @param registrationIds
     * @return
     */
    public boolean scheduleAndroidByAlias(Alert alert, Date time, Set<String> registrationIds) {
        PushPayload payload = buildAndroidPayload(alert, registrationIds);
        return schedule(time, payload);
    }

    /**
     * @param alert
     * @param badge
     * @param registrationIds
     * @return
     */
    public long sendIOSByAlias(Alert alert, Integer badge, Set<String> registrationIds) {
        PushPayload payload = buildIOSPayload(alert, badge, registrationIds);
        return send(payload);
    }

    /**
     * @param payload
     * @return
     */
    private long send(PushPayload payload) {
        for (int i = 0; i < retryCount; i++) {
            try {
                PushResult result = jpushClient.sendPush(payload);
                logger.info("Sent a notification to jpush, payload: "+ JSONUtils.toString(payload)+",retryCount:"+retryCount);
                logger.info("Sent a notification to jpush, result: {}, message id: {}, retryCount: {}", result.isResultOK(), result.msg_id ,retryCount);
                return result.msg_id;
            } catch (APIConnectionException e) {
                logger.error("Connection error. Should retry later. ", e);
                if (i < retryCount - 1) {
                    try {
                        TimeUnit.SECONDS.sleep(5 * i);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            } catch (APIRequestException e) {
                logger.info("HTTP Status: {}", e.getStatus(), e);
                if (e.getErrorCode() == 1011) {
                    break;
                }
                if (e.getStatus() == 429 || e.getStatus() == 502 || e.getStatus() == 503 || e.getStatus() == 504) {
                    try {
                        TimeUnit.SECONDS.sleep(10 * i);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
                if (i == retryCount - 1) {
                    return e.getMsgId();
                }
            }
        }
        return 0;
    }

    /**
     * @param time
     * @param payload
     * @return
     */
    private boolean schedule(Date time, PushPayload payload) {
        for (int i = 0; i < retryCount; i++) {
            try {
                String strTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(time);
                ScheduleResult result = jpushClient.createSingleSchedule(String.valueOf(time.getTime()), strTime, payload);
                logger.info("Sent a notification schedule to jpush, result: {}, schedule id: {}", result.isResultOK(), result.getSchedule_id());
                return result.isResultOK();
            } catch (APIConnectionException e) {
                logger.error("Connection error. Should retry later. ", e);
                if (i < retryCount - 1) {
                    try {
                        TimeUnit.SECONDS.sleep(5 * i);
                    } catch (InterruptedException e1) {
                        e1.printStackTrace();
                    }
                }
            } catch (APIRequestException e) {
                logger.info("HTTP Status: {}", e.getStatus(), e);
                if (e.getErrorCode() == 1011) {
                    break;
                }
                if (i == retryCount - 1) {
                    return false;
                }
            }
        }
        return false;
    }

    /**
     * 带标题的根据别名发送的消息。
     *
     * @param alert
     * @param registrationId
     * @return
     */
    public PushPayload buildAndroidPayload(Alert alert, Set<String> registrationId) {
        AndroidNotification.Builder ab = AndroidNotification.newBuilder().setAlert(alert.getTitle())/*.setTitle(alert.getTitle())*/.addExtra("t", alert.getNotificateType().getTypeCode()).addExtra("o", alert.getNotificateType().isOperationable());
        if (alert.getExtras() != null && !alert.getExtras().isEmpty()) {
            for (Map.Entry<String, String> entry : alert.getExtras().entrySet()) {
                ab.addExtra(entry.getKey(), entry.getValue());
            }
        }
        return PushPayload.newBuilder().setPlatform(Platform.android()).setAudience(Audience.registrationId(registrationId))
                .setNotification(Notification.newBuilder().addPlatformNotification(ab.build()).build()).build();
    }

    /**
     * @param alert
     * @param badge
     * @param registrationId
     * @return
     */
    public PushPayload buildIOSPayload(Alert alert, Integer badge, Set<String> registrationId) {
        IosNotification.Builder iosb = IosNotification.newBuilder();
        if (StringUtils.isNotEmpty(alert.getTitle())) {
            iosb.setAlert(alert.getTitle());
        } else {
            iosb.setAlert(alert.getContent());
        }
        if (badge != null) {
            iosb.setBadge(badge);
        }
        if (StringUtils.isNotEmpty(alert.getSound())) {
            iosb.setSound(alert.getSound());
        } else {
            iosb.disableSound();
        }
        iosb.addExtra("t", alert.getNotificateType().getTypeCode()).addExtra("o", alert.getNotificateType().isOperationable());
        if (alert.getExtras() != null && !alert.getExtras().isEmpty()) {
            for (Map.Entry<String, String> entry : alert.getExtras().entrySet()) {
                iosb.addExtra(entry.getKey(), entry.getValue());
            }
        }
        return PushPayload.newBuilder().setPlatform(Platform.ios()).setAudience(Audience.registrationId(registrationId))
                .setNotification(Notification.newBuilder().addPlatformNotification(iosb.build()).build())
                .setMessage(Message.content(alert.getContent())).setOptions(Options.newBuilder().setApnsProduction(apnsProduction).build()).build();
    }

    /**
     * 带标题的根据标签发送的消息。
     *
     * @param alert
     * @param tag
     * @return
     */
    public PushPayload buildAndroidPayload(Alert alert, String tag) {
        AndroidNotification.Builder ab = AndroidNotification.newBuilder().setAlert(alert.getTitle())/*.setTitle(alert.getTitle())*/.addExtra("t", alert.getNotificateType().getTypeCode()).addExtra("o", alert.getNotificateType().isOperationable());
        if (alert.getExtras() != null && !alert.getExtras().isEmpty()) {
            for (Map.Entry<String, String> entry : alert.getExtras().entrySet()) {
                ab.addExtra(entry.getKey(), entry.getValue());
            }
        }
        return PushPayload.newBuilder().setPlatform(Platform.android()).setAudience(Audience.tag(tag))
                .setNotification(Notification.newBuilder().addPlatformNotification(ab.build()).build()).build();
    }

    /**
     * @param alert
     * @param badge
     * @param tag
     * @return
     */
    public PushPayload buildIOSPayload(Alert alert, Integer badge, String tag) {
        IosNotification.Builder iosb = IosNotification.newBuilder();
        if (StringUtils.isNotEmpty(alert.getTitle())) {
            iosb.setAlert(alert.getTitle());
        } else {
            iosb.setAlert(alert.getContent());
        }
        if (badge != null) {
            iosb.setBadge(badge);
        }
        if (StringUtils.isNotEmpty(alert.getSound())) {
            iosb.setSound(alert.getSound());
        } else {
            iosb.disableSound();
        }
        iosb.addExtra("t", alert.getNotificateType().getTypeCode()).addExtra("o", alert.getNotificateType().isOperationable());
        if (alert.getExtras() != null && !alert.getExtras().isEmpty()) {
            for (Map.Entry<String, String> entry : alert.getExtras().entrySet()) {
                iosb.addExtra(entry.getKey(), entry.getValue());
            }
        }
        return PushPayload.newBuilder().setPlatform(Platform.ios()).setAudience(Audience.tag(tag))
                .setNotification(Notification.newBuilder().addPlatformNotification(iosb.build()).build())
                .setOptions(Options.newBuilder().setApnsProduction(apnsProduction).build()).build();
    }

    /**
     * 发送全体的消息。
     *
     * @return
     */
    private PushPayload buildAllPayload(Alert alert, Integer badge, Set<String> registrationId) {
        String[] array = new String[registrationId.size()];
        registrationId.toArray(array);
        return buildAllPayload(alert, badge, array);
    }

    /**
     * @param alert
     * @param badge
     * @param registrationId
     * @return
     */
    private PushPayload buildAllPayload(Alert alert, Integer badge, String... registrationId) {
        IosNotification.Builder iosb = IosNotification.newBuilder();
        if (StringUtils.isNotEmpty(alert.getTitle())) {
            iosb.setAlert(alert.getTitle());
        } else {
            iosb.setAlert(alert.getContent());
        }
        if (badge != null) {
            iosb.setBadge(badge);
        }
        if (StringUtils.isNotEmpty(alert.getSound())) {
            iosb.setSound(alert.getSound());
        } else {
            iosb.disableSound();
        }
        iosb.setContentAvailable(true);
        iosb.addExtra("t", alert.getNotificateType().getTypeCode()).addExtra("o", alert.getNotificateType().isOperationable());
        AndroidNotification.Builder ab = AndroidNotification.newBuilder().setAlert(alert.getTitle())/*.setTitle(alert.getTitle())*/
                .addExtra("t", alert.getNotificateType().getTypeCode()).addExtra("o", alert.getNotificateType().isOperationable())
                .addCustom("uri_activity","com.bsoft.hcn.pub.push.OpenClickActivity")
                ;
        if (alert.getExtras() != null && !alert.getExtras().isEmpty()) {
            for (Map.Entry<String, String> entry : alert.getExtras().entrySet()) {
                iosb.addExtra(entry.getKey(), entry.getValue());
                ab.addExtra(entry.getKey(), entry.getValue());
            }
        }
        return PushPayload.newBuilder().setPlatform(Platform.all()).setAudience((registrationId == null || registrationId.length == 0) ? Audience.all() : Audience.registrationId(registrationId))
                .setNotification(Notification.newBuilder().addPlatformNotification(iosb.build()).addPlatformNotification(ab.build()).build())
                .setOptions(Options.newBuilder().setApnsProduction(apnsProduction).build()).build();
    }
}
